/*  */
#include "graph.h"

/* document pointer */
#include <libxml/tree.h>
xmlDocPtr doc=NULL; 

/* global variables */
vertex **vertices;
edge **edges;
int param; /*parameter for handling error*/
int nVertices, nEdges;
int *clustersColors = NULL;

/* ev = edges & vertices*/
int evCurrentSize[NUM_OF_ARRAYS]={0, 0}, evHead[NUM_OF_ARRAYS]={0, 0};
int evOpenSlotPtr[NUM_OF_ARRAYS]={0, 0}, evCounter[NUM_OF_ARRAYS] = {0, 0};
  

ERROR_CODE addVertex(char name[])
{
    bool bRes;
    int vHead=evHead[1], vCurrentSize=evCurrentSize[1];
    int vOpenSlot = evOpenSlotPtr[1];

    if (!isName(name))
    {
        return CMD_VRTX_NAME_NOT_VALID;
    }

    /* There is an open slot to insert the new vertex (insted of an old deleted vertex)*/
    if(vOpenSlot < vHead)
    {
        vertices[vOpenSlot]->name = (char *) malloc(strlen(name) + 1);
        if (vertices[vOpenSlot]->name == NULL)
        {
            return MEM_ALLOC_FAIL;
        }

        strcpy(vertices[vOpenSlot]->name, name);
        vertices[vOpenSlot]->deleted = false;

        updateOpenSlot(_VERTEX);
    }

    /* No open slots are available, a new vertex should be allocated */
    else
    {
        /* No more room in vertices, extend array is needed */
        if (vHead == vCurrentSize)
        {
            bRes = extendArray(_VERTEX);
            if (bRes != true)
            {
                return bRes;
            }
        }

        /* building the next vertex */
        vertices[vHead] = (vertex *) malloc(sizeof(struct vertex_t));
        if (vertices[vHead] == NULL)
        {
            return MEM_ALLOC_FAIL;
        }

        vertices[vHead]->name = (char*)malloc(strlen(name)+1);
        if (vertices[vHead]->name == NULL)
        {
            return MEM_ALLOC_FAIL;
        }

        strcpy(vertices[vHead]->name, name);
        vertices[vHead]->deleted = false;
        vertices[vHead]->cluster = 1;

        evHead[_VERTEX]++;
    }

    evCounter[_VERTEX]++;
    return true;
}

ERROR_CODE addEdge(char v1[], char v2[], char weight[])
{
    int i, vHead = evHead[_VERTEX], eHead = evHead[_EDGE];
    int id1 = -1, id2 = -1;
    int eOpenSlot = evOpenSlotPtr[_EDGE], eCurrentSize = evCurrentSize[_EDGE];
    double w;
    bool bRes;

    /* weight is a number*/
    if (!isDouble(weight))
    {
        return WEIGHT_NOT_NUMBER;
    }
    /* convert weight to double */
    w = atof(weight);

    /* weight is negative */
    if (w < 0)
    {
        return INVALID_WEIGHT;
    }

    /* case of two names*/
    if (isName(v1) && isName(v2))
    {

        for (i = 0; i < vHead ; i++)
        {
            if (!strcmp(vertices[i]->name,v1))
            {
                /*case of duplicate names of v1*/
                if (id1 >= 0)
                {
                    return VRTX_DUP_NAMES;
                }
                id1 = i;
            }

            if (!strcmp(vertices[i]->name,v2))
            {
                /*case of duplicate names of v2*/
                if (id2 >= 0)
                {
                    return VRTX_DUP_NAMES;
                }
                id2 = i;
            }
        }

        /* one of the vertices names doesnt exist */
        if (id1 == -1 || id2 == -1)
        {
            return EDGE_VRTX_NOT_FOUND;
        }
    }

    /* case of two id's*/
    else if (isNumber(v1) && isNumber(v2))
    {
        id1 = atoi(v1);
        id2 = atoi(v2);

        if (id1 < 0 || id2 < 0 || id1 >= vHead || id2 >= vHead)
        {
            return EDGE_VRTX_NOT_FOUND;
        }
    }

    else
    {
        return CMD_FORMAT_INVALID;
    }

    /* at least one of the vertices has been deleted */
    if ((vertices[id1]->deleted == true) || (vertices[id2]->deleted == true))
    {
        return EDGE_VRTX_NOT_FOUND;
    }

    /* case of self loop */
    if (id1 == id2)
    {
        return LOOP_NOT_ALLOWED;
    }

    /* check for duplicated edges */
    for (i = 0 ; i < eHead ; i++)
    {
        if (((edges[i]->v1_id == id1 && edges[i]->v2_id == id2) || (edges[i]->v1_id == id2 && edges[i]->v2_id == id1))
                && (edges[i]->deleted == false))
        {
            return EDGE_DUP;
        }
    }

    /*adding the new edge */
    /* There is an open slot to insert the new edge (insted of an old deleted edge)*/
    if(eOpenSlot < eHead)
    {
        edges[eOpenSlot]->v1_id = id1;
        edges[eOpenSlot]->v2_id = id2;
        edges[eOpenSlot]->deleted = false;
        edges[eOpenSlot]->weight = w;

        updateOpenSlot(_EDGE);
    }
    /* No open slots are available, a new edge should be allocated */
    else
    {
        /* No more room in edges, extend array is needed */
        if (eHead == eCurrentSize)
        {
            bRes = extendArray(_EDGE);
            if (bRes != true)
            {
                return bRes;
            }
        }

        /* building the next vertex */
        edges[eHead] = (edge *) malloc(sizeof(struct edge_t));
        if (edges[eHead] == NULL)
        {
            return MEM_ALLOC_FAIL;
        }

        edges[eHead]->v1_id = id1;
        edges[eHead]->v2_id = id2;
        edges[eHead]->deleted = false;
        edges[eHead]->weight = w;

        evHead[_EDGE]++;
    }

    evCounter[_EDGE]++;
    return true;

}

void print(bool isCluster)
{
    int i;

    printf("%d vertices:\n", evCounter[_VERTEX]);

    for (i = 0; i < evHead[_VERTEX]; i++)
    {
        if (vertices[i]->deleted == false)
        {
            printf("%d: %s", i, vertices[i]->name);

            if (isCluster)
            {
                printf(" %d", vertices[i]->cluster);
            }

            printf("\n");
        }

    }

    if (evCounter[_EDGE] > 0)
    {
        printf("%d edges:\n", evCounter[_EDGE]);

        for (i = 0; i < evHead[_EDGE]; i++)
        {
            if (edges[i]->deleted == false)
            {
                printf("%d: %s-%s %.3f\n", i, vertices[edges[i]->v1_id]->name, vertices[edges[i]->v2_id]->name, edges[i]->weight);
            }
        }
    }

}

void quitProperly()
{
    if (evCurrentSize[_VERTEX] != 0)
    {
        freeArray(_VERTEX);
    }

    if (evCurrentSize[_EDGE] != 0)
    {
        freeArray(_EDGE);
    }
}

bool handleError(bool condition, ERROR_CODE errorNumber)
{
    if (condition)
    {
        return true;
    }

    switch (errorNumber)
    {
        /* dynamic memory allocation errors */
        case MEM_ALLOC_FAIL :
        {
            printf("Error: Memory allocation failed\n");
            break;
        }

        /* receiving new command erros*/
        case CMD_NOT_RECOGNIZED :
        {
            printf("Error: Command is not recognized\n");
            break;
        }
        case CMD_FORMAT_INVALID :
        {
            printf("Error: Command format is not valid\n");
            break;
        }
        case INVALID_WEIGHT :
        {
            printf("Error: When adding an edge wieght must be positive\n");
            break;
        }
        case WEIGHT_NOT_NUMBER :
        {
            printf("Error: When adding an edge wieght must be a number\n");
            break;
        }
        case CMD_VRTX_NAME_NOT_VALID :
        {
            printf("Error: When adding an vertex name must have at least one letter\n");
            break;
        }
        case EDGE_MUST_BE_NUMBER :
        {
            printf("Error: Edge id must be a number\n");
            break;
        }
        case VRTX_ID_MUST_BE_NUM :
        {
            printf("Error: Vertex id must be a number\n");
            break;
        }
        case INVALID_NUM_OF_CLUSTERS :
        {
            printf("Error: Number of clusters must be a number bigger or equal to 1\n");
            break;
        }

        /* remove vertex errors */
        case VRTX_NOT_FOUND :
        {
            printf("Error: Requested vertex does not exist\n");
            break;
        }
        case VRTX_DUP_NAMES :
        {
            printf("Error: More than one vertex with the requested name exists, please remove using id\n");
            break;
        }
        case RMV_VRTX_EDGES :
        {
            printf("Error: Cannot delete vertex %s since there are edges connected to it\n", vertices[param]->name);
            break;
        }

        /* errors in add_edge */
        case EDGE_VRTX_NOT_FOUND :
        {
            printf("Error: First/second vertex was not found\n");
            break;
        }
        case LOOP_NOT_ALLOWED :
        {
            printf("Error: No self loops are allowed in the network\n");
            break;
        }
        case EDGE_DUP :
        {
            printf("Error: No duplicate edges are allowed in the network\n");
            printf("       Edge id: %d\n", nEdges);
            nEdges--; 
            return true;
            break;
        }
        case VRTX_DUP_NAMES_ADD :
        {
            printf("Error: More than one vertex with the requested name exists, please add edge using vertices id\n");
            break;
        }

        /* errors in remove_edge */
        case EDGE_NOT_FOUND :
        {
            printf("Error: Requested edge does not exist\n");
            break;
        }
        default :
        {
            break;
        }
    }
    return false;
}

bool checkForEdges(int id)
{
    int i;
    for (i = 0; i< evHead[_EDGE] ; i++)
    {
        if (edges[i]->v1_id == id || edges[i]->v2_id == id)
        {
            return true;
        }
    }

    return false;
}

void freeArray(bool isVertices)
{
    int i;

    if (isVertices)
    {
        for(i=0; i<evHead[_VERTEX]; i++)
        {

            if (vertices[i]->deleted == false)
            {
                free(vertices[i]->name);
            }
            free(vertices[i]);
        }
        free(vertices);
    }

    else
    {
        for (i=0; i<evHead[_EDGE]; i++)
        {
            free(edges[i]);
        }
        free(edges);
    }
}

ERROR_CODE extendArray(bool isVertices)
{
    /* Extend vertices */
    if (isVertices){

		vertices = (vertex**)realloc(vertices, (evCurrentSize[_VERTEX]+MEMORY_CHUNK_SIZE) * sizeof(struct vertex_t *));

        evOpenSlotPtr[_VERTEX] = (evOpenSlotPtr[_VERTEX] < evCurrentSize[_VERTEX]) ? evOpenSlotPtr[_VERTEX] : evCurrentSize[_VERTEX] + MEMORY_CHUNK_SIZE;

        evCurrentSize[_VERTEX] += MEMORY_CHUNK_SIZE;

    }
    /* Extend edges */
    else{

		edges = (edge**)realloc(edges, (evCurrentSize[_EDGE]+MEMORY_CHUNK_SIZE) * sizeof(struct edge_t *));

        evOpenSlotPtr[_EDGE] = (evOpenSlotPtr[_EDGE] < evCurrentSize[_EDGE]) ? evOpenSlotPtr[_EDGE] : evCurrentSize[_EDGE] + MEMORY_CHUNK_SIZE;

        evCurrentSize[_EDGE] += MEMORY_CHUNK_SIZE;

    }

    return true;
}

void updateOpenSlot(int isVertices)
{
    int i;

    for(i = evOpenSlotPtr[isVertices]; i< evHead[isVertices]; i++)
    {
        /* looking for open slot in vertices */
        if (isVertices)
        {
            if (vertices[i]->deleted == true)
            {
                evOpenSlotPtr[isVertices] = i;
                return;
            }
        }
        /* looking for open slot in edges */
        else
        {
            if (edges[i]->deleted == true)
            {
                evOpenSlotPtr[isVertices] = i;
                return;
            }
        }
    }

    /* no open slots found */
    evOpenSlotPtr[isVertices] = evCurrentSize[isVertices];
}

bool isName(char name[])
{
    int i;
    char c;

    for (i=0; i<strlen(name); i++)
    {
        c = name[i];
        if ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))
        {
            return true;
        }
    }

    return false;
}

bool isDouble(char weight[])
{
    int i=0;

    for (i = 0 ; i < strlen(weight); i++)
    {
        if (weight[i] == '.')
        {
            i++;
            break;
        }
        if (weight[i] > '9' && weight[i] < '0')
        {
            return false;
        }
    }

    for (;i < strlen(weight); i++)
    {
        if (weight[i] > '9' && weight[i] < '0')
        {
            return false;
        }
    }
    return true;
}

bool isNumber(char name[])
{
    int i;

    for (i = 0; i < strlen(name) ; i++)
    {
        if (name[i] < '0' || name[i] > '9')
        {
            return false;
        }
    }
    return true;
}

int getVertexID(int e, int v)
{
    if (v > 1)
    {
        return v;
    }
    return (v==0) ? edges[e]->v1_id : edges[e]->v2_id;
}

double getEdgeWeight(int e)
{
    return edges[e]->weight;
}

/* Parse file */
int parseFile(char *filename, bool isVertices)
{
    FILE *file;
    char *buffer=NULL, *line=NULL, v1[MAX_ARG_SIZE], v2[MAX_ARG_SIZE], w[MAX_ARG_SIZE];
    int fileSize;
    bool bRes;

    
    file = fopen(filename, "r");
    if (file == NULL)
    {
        return -1;
    }

    fseek(file, 0, SEEK_END);
    fileSize = ftell(file);
    fseek(file, 0, SEEK_SET);

    buffer = (char*)malloc(sizeof(char)*fileSize+1);

    if (fread(buffer, sizeof(char), fileSize, file) != fileSize) /* read all file into buffer */
    {
        return -1;
    }

    
    if (isVertices)
    {
        nVertices = 0;
        
        line = strtok(buffer, "\n");
        while (line != NULL)
        {
            bRes = addVertex(line);
            bRes = handleError(bRes == 1, bRes);
            
            if (bRes != true)
            {
                if (buffer != NULL)
                {
                    free(buffer);
                }
                nVertices = 0;
                return -1;
            }
            nVertices++;
            line = strtok(NULL, "\n");
        }

    }
    else
    {
        nEdges = 0;
        line = strtok(buffer, "\n");
        while (line != NULL)
        {
            sscanf(line, "%s %s %s", v1, v2, w);
        
            bRes = addEdge(v1, v2, w);
            bRes = handleError(bRes == 1, bRes);
            
            if (bRes != true)
            {
                
                if (buffer != NULL)
                {
                    free(buffer);
                }

                nEdges = 0;
                return -1;
            }
            
            nEdges++;
            line = strtok(NULL, "\n");
        }
    }
    
    if (buffer != NULL)
        free(buffer);
    
    return 1;
}

int handleInput(char *inputFolder)
{
    int status;
    char nodesFile[CMD_LINE_MAX_SIZE], edgesFile[CMD_LINE_MAX_SIZE];

    /* build path to nodes and edges files*/
    strcpy(nodesFile, inputFolder);
    strcat(nodesFile, "/nodes");
    strcpy(edgesFile, inputFolder);
    strcat(edgesFile, "/edges");

    /* parse files and build the graph*/
    status = parseFile(nodesFile, _VERTEX);
    if(status < 0){
        fprintf (stderr, "Failed to parse nodes\n");
        return 1;
    }
    
    status = parseFile(edgesFile, _EDGE);
    if(status < 0){
        fprintf (stderr, "Failed to parse edges\n");
        return 1;
    }
    
    return 0;
}

int getNumVertices()
{
    return nVertices;
}

int getNumEdges()
{
    return nEdges;
}

void printIntArray(int arr[], int arrLength)
{
    int i;
    for(i=0; i<arrLength; i++){

        printf("[%d] %d\n", i, arr[i]);
    }
    printf("\n");

}

void printCharArray(char arr[], int arrLength)
{
    int i;
    for(i=0; i<arrLength; i++){

        printf("[%d] %c\n", i, arr[i]);
    }
    printf("\n");

}

void printDoubleArray(double arr[], int arrLength)
{
    int i;
    for(i=0; i<arrLength; i++){

        printf("[%d] %f\n", i, arr[i]);
    }
    printf("\n");

}


int updateNetworkClusters(int kMax, double *x)
{
    int k, j, i;
    
    for (k=1, j=0; k<=kMax; k++)
    {
        for (i=0; i<nEdges; i++, j++);
        
        for (i=0; i<nVertices; i++, j++)
        {            
            if (IS_VALUE_1(x[j]))
            {
                vertices[i]->cluster = k;
            }
        }
    }
    
    return 0;
}



int createResultsFile(char *filename, double *objvals, int LB, int UB)
{
    FILE *file;
    int i, lb, nBytes = 0, nxzBytes = 0, status=0;
    char *buffer, *xzBuffer;
    
    
    file = fopen(filename, "w");
    if (file == NULL)
    {
        status = 1;
        fprintf (stderr, "Error: open file\n");
        goto TERMINATE;
    }
    
    buffer = (char*) calloc(UB-LB+1, sizeof(char)*CMD_LINE_MAX_SIZE);
    if (buffer == NULL)
    {
        status = 1;
        fprintf (stderr, "Error: Memory allocation failed\n");
        goto TERMINATE;
    }
    
  
    /* writing to buffer */
    for (i = 0, lb = LB; i <= (UB - LB) ; i++, lb++)
    {
        sprintf(buffer, "%s%d: %.3f\n", buffer, lb, objvals[i]);
    }
    nBytes = sprintf(buffer, "%s\nThe clustered network for %d:\n", buffer, UB);
    
    if (fwrite(buffer, sizeof(char), nBytes, file) != nBytes)
    {
        fprintf (stderr, "Error: couldnt write to file\n");
        status = 1;
        goto TERMINATE;
    }
    
    xzBuffer = (char*) calloc(nVertices+nEdges+3, sizeof(char) * CMD_LINE_MAX_SIZE);
    if (xzBuffer == NULL)
    {
        fprintf (stderr, "Error: Memory allocation failed\n");
        status = 1;
        goto TERMINATE;
    }
    
    nxzBytes = printResults(xzBuffer);
     
    if (fwrite(xzBuffer, sizeof(char), nxzBytes, file) != nxzBytes)
    {
        fprintf (stderr, "Error: couldnt write to file\n");
        status = 1;
        goto TERMINATE;
    }
   
    
TERMINATE:
    
    if (buffer != NULL)
    {
        free(buffer);
    }
  
    if (xzBuffer != NULL)
    {
        free(xzBuffer);
    }
   
    fclose(file);
    
    return status;
}


int printResults(char *resBuffer)
{
    int i, nBytes=0;
    
    nBytes += sprintf(resBuffer, "%d vertices:\n", evCounter[_VERTEX]);
    
    for (i = 0; i < evHead[_VERTEX]; i++)
    {
        if (vertices[i]->deleted == false)
        {
            nBytes += sprintf(resBuffer, "%s%d: %s %d\n", resBuffer, i, vertices[i]->name, vertices[i]->cluster);
        }
    }
    
    if (evCounter[_EDGE] > 0)
    {
        nBytes += sprintf(resBuffer, "%s%d edges:\n", resBuffer, evCounter[_EDGE]);
        
        for (i = 0; i < evHead[_EDGE]; i++)
        {
            nBytes = sprintf(resBuffer, "%s%d: %s-%s %.3f\n", resBuffer, i, vertices[edges[i]->v1_id]->name, vertices[edges[i]->v2_id]->name, edges[i]->weight);
        }
    }
    
    return nBytes;
    
}


int buildClustersColorsArray(int nClusters)
{
    int count=0, j, i, maxSize=0, tmp=0, status=0;
    int *A=NULL, *B=NULL, *C=NULL, *D=NULL;
    
    clustersColors = (int*) calloc(nClusters+1, sizeof(int));
    if (clustersColors == NULL)
    {
        status = 1;
        goto TERMINATE;
    }
    
    A = (int *) calloc(nClusters+1, sizeof(int));
    if (A == NULL)
    {
        status = 1;
        goto TERMINATE;
    }
 
    /* building A */    
    for (i = 0 ; i < nVertices ; i++)
    {
        A[vertices[i]->cluster]++;
        
        maxSize = (A[vertices[i]->cluster] > maxSize) ? A[vertices[i]->cluster] : maxSize;
    }
    
    B = (int *) calloc(maxSize+1, sizeof(int));
    C = (int *) calloc(nClusters+1, sizeof(int));
    D = (int *) calloc(maxSize+1, sizeof(int));
  
    if (B == NULL || C == NULL || D == NULL)
    {
        status = 1;
        goto TERMINATE;
    }    
    /* building B */
    for (i = 1 ; i <= nClusters ; i++)
    {
        B[A[i]]++;
    }
    /* building C*/
    for (i = maxSize, j=0 ; i > 0 ; i--)
    {
        count = B[i];
        for(; count > 0; j++, count--)
        {
            C[j] = i;

        }
    }
    /* building D */
    j = -1;
    for (i = 0; i <= nClusters ; i++)
    {
        if (j != C[i])
        {
            D[C[i]] = i;
            j = C[i];
        }
    }
    
    /* manipulate C */
    for (i = 0 ; i <= nClusters ; i++)
    {
        C[i] = i+1;
    }
    
    /* building cluster colors */
    for (i = 1; i <= nClusters ; i++)
    {
        tmp = A[i];
        clustersColors[i] = C[D[tmp]];
        D[tmp]++;
        
        /*clustersColors[i] = C[D[A[i]]++];*/
        
    }
    
TERMINATE:
    
    if (A != NULL) free(A);
    if (B != NULL) free(B);
    if (C != NULL) free(C);
    if (D != NULL) free(D);
    
    return status;
}

int getColorHex(char *buffer, int v_id)
{
    int colorNumber;
    
    colorNumber = clustersColors[vertices[v_id]->cluster];
    
    switch (colorNumber) 
    {
        case 1:
            sprintf(buffer, "%s", AQUA);
            break;
        case 2:
            sprintf(buffer, "%s", BLUE);
            break;
        case 3:
            sprintf(buffer, "%s", BLUEVIOLET);
            break;
        case 4:
            sprintf(buffer, "%s", BROWN);
            break;
        case 5:
            sprintf(buffer, "%s", CHARTREUSE);
            break;
        case 6:
            sprintf(buffer, "%s", DARKGREEN);
            break;
        case 7:
            sprintf(buffer, "%s", GOLD);
            break;
        case 8:
            sprintf(buffer, "%s", HOTPINK);
            break;
        case 9:
            sprintf(buffer, "%s", ORANGERED);
            break;
        default:
            sprintf(buffer, "%s", SILVER);
            break;
    }
    
    return colorNumber;
}

int buildXGMMLSolution(int nClusters, char *outputFolder, char bound)
{

    
    xmlNodePtr root_node = NULL, node = NULL, edge = NULL, graphicsNode = NULL, nextSibling = NULL;/* node pointers */
    char buffer[512];
    int i, colorNumber, status=0;
    
    
    status = buildClustersColorsArray(nClusters);
    if(status) {
        goto TERMINATE;
    }
    
    /* 
     * Creates a new document, a node and set it as a root node
     */
    if (bound == 'L'){
        doc = xmlNewDoc(BAD_CAST "1.0");
        root_node = xmlNewNode(NULL, BAD_CAST "graph");
        xmlDocSetRootElement(doc, root_node);
        
        xmlNewProp(root_node, BAD_CAST "directed", BAD_CAST "0");
        
        xmlNewNs(root_node, BAD_CAST "http://www.cs.rpi.edu/XGMML", NULL);
        xmlNewNs(root_node, BAD_CAST "http://www.w3.org/1999/xlink", BAD_CAST "xlink");
        xmlNewNs(root_node, BAD_CAST "http://www.w3.org/1999/02/22-rdf-syntax-ns#", BAD_CAST "rdf");
        xmlNewNs(root_node, BAD_CAST "http://purl.org/dc/elements/1.1/", BAD_CAST "dc");
        xmlNewNs(root_node, BAD_CAST "http://www.cytoscape.org", BAD_CAST "cy");
        
        sprintf(buffer, "%d_clustering_solution", nClusters);
        xmlNewProp(root_node, BAD_CAST "label", BAD_CAST buffer);
    }
    else
    {
        
        root_node = xmlDocGetRootElement(doc);
        
        if(bound == 'U')
        {
            sprintf(buffer, "best_clusters");
        }
        else
        {
            sprintf(buffer, "%d_clustering_solution", nClusters);
        }
        
        xmlSetProp(root_node, BAD_CAST "label", BAD_CAST buffer);
        
        node = xmlFirstElementChild(root_node);
    }
    
    
    /* 
     * xmlNewChild() creates a new node, which is "attached" as child node
     * of root_node node. 
     */
    
    for (i = 0 ; i < nVertices ; i++)
    {
        if (bound == 'L'){
            node = xmlNewChild(root_node, NULL, BAD_CAST "node", NULL);
            
            xmlNewProp(node, BAD_CAST "label", BAD_CAST vertices[i]->name);
            
            sprintf(buffer, "%d", (i+1));
            xmlNewProp(node, BAD_CAST "id", BAD_CAST buffer);
            
            graphicsNode = xmlNewChild(node, NULL, BAD_CAST "graphics", NULL);
            
            xmlNewProp(graphicsNode, BAD_CAST "type", BAD_CAST "ELLIPSE");
            getColorHex(buffer, i);
            xmlNewProp(graphicsNode, BAD_CAST "fill", BAD_CAST buffer);
            xmlNewProp(graphicsNode, BAD_CAST "width", BAD_CAST "4");
            xmlNewProp(graphicsNode, BAD_CAST "cy:nodeLabel", BAD_CAST vertices[i]->name);
            xmlNewProp(graphicsNode, BAD_CAST "cy:borderLineType", BAD_CAST "solid");
        }
        else{
            
            colorNumber = getColorHex(buffer, i);
            
            if (bound == 'U' && colorNumber > 5){
                
                nextSibling = xmlNextElementSibling(node);
                
                xmlUnlinkNode(node);
                xmlFreeNode(node);
                node = nextSibling;
                continue;
            }
            
            graphicsNode = xmlFirstElementChild(node);
            xmlSetProp(graphicsNode, BAD_CAST "fill", BAD_CAST buffer);
            node = xmlNextElementSibling(node);
            
        }
    }
    
    
    for (i = 0; i < nEdges; i++)
    {
        if (bound == 'L'){
            edge = xmlNewChild(root_node, NULL, BAD_CAST "edge", NULL);
            
            sprintf(buffer, "%s (pp) %s", vertices[edges[i]->v1_id]->name, vertices[edges[i]->v2_id]->name);
            xmlNewProp(edge, BAD_CAST "label", BAD_CAST buffer);
            
            sprintf(buffer, "%d", (edges[i]->v1_id)+1);
            xmlNewProp(edge, BAD_CAST "source", BAD_CAST buffer);
            
            sprintf(buffer, "%d", (edges[i]->v2_id)+1);
            xmlNewProp(edge, BAD_CAST "target", BAD_CAST buffer);
            
            graphicsNode = xmlNewChild(edge, NULL, BAD_CAST "graphics", NULL);
            
            sprintf(buffer, "weight=%.3f", edges[i]->weight);
            xmlNewProp(graphicsNode, BAD_CAST "cy:edgeLabel", BAD_CAST buffer);
        }
        else if (bound == 'U'){
            
            nextSibling = xmlNextElementSibling(node);
            
            if (getColorHex(buffer,edges[i]->v1_id) > 5 || getColorHex(buffer, edges[i]->v2_id) > 5){
                
                xmlUnlinkNode(node);
                xmlFreeNode(node);
            }
            
            node = nextSibling;
        }
    }
    /* 
     * Dumping document to stdio or file
     */
    
    if(bound == 'U')
    {
        sprintf(buffer, "%s/best_clusters.xgmml", outputFolder);
    }
    else
    {
        sprintf(buffer, "%s/%d_clustering_solution.xgmml", outputFolder, nClusters);
    }
    
    xmlSaveFormatFileEnc(buffer, doc, "utf-8", 1);
    
    
TERMINATE:
    
    if (bound == 'U' || (status && bound == 'K')){
        
        /*free the document */
        xmlFreeDoc(doc);
        
        /*
         *Free the global variables that may
         *have been allocated by the parser.
         */
        xmlCleanupParser();
    }
    
    if (clustersColors != NULL)
    {
        free(clustersColors);
    }
    
    
    return status;
}



